(function (window, undefined, fruit, Binding, util, Formatter, fruitElement, dom, ui) {
    var emptyElement = fruit.ui.EmptyElement;
    var VIEW_ENGINE_REMARK = "__VIEW_ENGINE_REMARK__";
    var sequence = 1;
    var dynamicMethods = {
        find: function (inName) {
            if (!inName) {
                return this._$.$root;
            }
            var view = this._$[inName];
            return view ? view : emptyElement;
        },
        setRegion: function (inComponent, inName) {
            var metaData = this.$view[VIEW_ENGINE_REMARK];
            var target, defaultRegion;
            if (inName) {
                target = this.$(inName);
            } else {
                //default region
                defaultRegion = metaData.defaultRegion;
                if (defaultRegion == "") {
                    target = this.$();
                } else if (defaultRegion) {
                    target = this.$(defaultRegion);
                }
            }
            target.empty();
            target.appendChild(inComponent);
        }
    };
    var classValueToMap = function (inValue) {
        var array, i, item, length;
        var map = {};
        if (!util.isString(inValue)) {
            throw "class value must be String type."
        }
        array = inValue.split(" ");
        length = array.length;
        for (i = 0; i < length; i++) {
            item = array[i];
            if (!item) {
                continue;
            }
            map["cls" + (sequence++)] = item;
        }
        return map;
    };
    var tryFormatter = function (inValue) {
        var value = {
            value: inValue
        };
        if (!util.isString(inValue)) {
            return value;
        }
        var formatter = new Formatter(inValue);

        var keys = formatter.keys();
        if (!keys || keys == 0) {
            return value;
        }
        value.formatter = formatter;
        return value;
    }
    var parseSimpleValue = function (inValue, inParams, inPath, inAttrName) {
        var result = tryFormatter(inValue);
        var value = result.value;
        var id, keys, length;
        var formatter = result.formatter;
        var info;
        var json;
        var formatter = result.formatter;
        if (formatter) {
            info = {
                targetElementPath: inPath,
                targetProperty: inAttrName,
                formatter: formatter
            };
            keys = formatter.keys();
            length = keys.length;
            if (length > 1) {
                throw "only support 1 key.";
            } else {
                value = formatter.formatObj(inParams.options);
                inParams.formatterList.push(info);
                info.sourcePath = keys[0].path;
            }
        } else {
            if (fruitElement.has(inAttrName)) {
                json = inParams.elementPropertyMap[inPath];
                if (!json) {
                    json = inParams.elementPropertyMap[inPath] = {};
                }
                json[inAttrName] = inValue;
                value = null;
            }
        }
        return value;
    };
    var attrParser = {
        parseAttr: function (inValue, inParams, inPath, inAttrName) {
            if (!inValue) {
                return inValue;
            }
            var parser = this[inAttrName];
            if (!parser) {
                parser = this.defaultParser;
            }
            return parser.call(this, inValue, inParams, inPath, inAttrName);
        },
        _parse: function (inValue, inParams, inPath, inAttrName) {
            var json, value;
            if (util.isString(inValue)) {
                return parseSimpleValue(inValue, inParams, inPath, inAttrName);
            } else if (util.isObject(inValue)) {
                json = inParams.elementPropertyMap[inPath];
                if (!json) {
                    json = inParams.elementPropertyMap[inPath] = {};
                }
                json[inAttrName] = inValue;
                value = null;
                return value;
            }
        },
        template: function (inValue, inParams, inPath, inAttrName) {
            return this._parse(inValue, inParams, inPath, inAttrName);
        },
        items: function (inValue, inParams, inPath, inAttrName) {
            return this._parse(inValue, inParams, inPath, inAttrName);
        },
        "class": function (inValue, inParams, inPath, inAttrName) {
            var def = {}, name, value, keys, length;
            var clsClass = "";
            var cssClassMap = inParams.cssClassMap;
            var result;
            var info, formatter, id, length;
            var map = classValueToMap(inValue);
            for (name in map) {
                value = map[name];
                result = tryFormatter(value);
                formatter = result.formatter;
                if (formatter) {
                    info = {
                        targetElementPath: inPath,
                        targetProperty: inAttrName,
                        formatter: formatter
                    };
                    keys = formatter.keys();
                    length = keys.length;
                    if (length > 1) {
                        throw "only support 1 key.";
                    } else {
                        value = formatter.formatObj(inParams.options);
                        id = formatter.$id;
                        inParams.formatterList.push(info);
                        info.sourcePath = keys[0].path;
                        info.subProperty = name;
                    }
                    def[name] = value;
                } else {
                    clsClass += value + " ";
                    def[name] = value;
                }
            }
            if (clsClass.length > 0) {
                clsClass = clsClass.substring(0, clsClass.length - 1);
            }
            cssClassMap[inPath] = def;
            return clsClass;
        },
        defaultParser: function (inValue, inParams, inPath, inAttrName) {
            if (util.isObject(inValue)) {
                return this._parseJSONValue(inValue, inParams, inPath, inAttrName);
            } else {
                return parseSimpleValue(inValue, inParams, inPath, inAttrName);
            }
        },
        _parseJSONValue: function (inValue, inParams, inPath, inAttrName) {
            var name, value, result;
            var info, formatter;
            var keys, length;
            var array = [];
            for (name in inValue) {
                value = inValue[name];
                result = tryFormatter(value);
                formatter = result.formatter;
                if (formatter) {
                    info = {
                        targetElementPath: inPath,
                        targetProperty: inAttrName,
                        formatter: formatter
                    };
                    keys = formatter.keys();
                    length = keys.length;
                    if (length > 1) {
                        throw "only support 1 key.";
                    } else {
                        value = formatter.formatObj(inParams.options);
                        inParams.formatterList.push(info);
                        info.sourcePath = keys[0].path;
                        info.subProperty = name;
                    }
                } else {
                    if (value) {
                        array.push(name, ':', value, ';');
                    }
                }
            }
            return array.join("");
        }
    };
    var parser = {
        fromComponent: function (inClazz, inName) {
            var Component = getComponent(inName);
            var view = Component.prototype.$view;
            var remark = view[VIEW_ENGINE_REMARK];
            delete view[VIEW_ENGINE_REMARK];
            var json = util.deepClone(view);
            if (remark) {
                view[VIEW_ENGINE_REMARK] = remark;
            }
            inClazz.prototype.$view = json;
            this.fromJSON(json);
            return json;
        },
        fromFunction: function (inClazz, inFn,inTypeId) {
            var superType = inClazz.$super;
            var superView = superType.prototype.$view;
            var json = superView;
            if (util.isFunction(superView)) {
                json = parser.fromFunction(superType, superView);
            }
            var remark = json[VIEW_ENGINE_REMARK];
            delete json[VIEW_ENGINE_REMARK];
            var json = util.deepClone(json);
            if (remark) {
                json[VIEW_ENGINE_REMARK] = remark;
            }

            var context = new ViewExtendContext(json);
            inFn.call(context);
            json = inClazz.prototype.$view = context.getJson();
            this.fromJSON(json, inClazz.$type,inTypeId);
            return json;
        },
        fromJSON: function (inJson, inType, inTypeId) {
            //TODO check options,maybe remove
            var options = inJson.options;
            var params = this.createInitParams(options);
            params.element = this.createElement(inJson, false, params, "", inType, inTypeId);
            inJson[VIEW_ENGINE_REMARK] = params;
        },
        createInitParams: function (inOpts) {
            return {
                cssClassMap: {},
                namePathMap: {},
                regions: {},
                formatterList: [],
                formatterPositionMap: {},
                elementPropertyMap: {},
                components: [],
                events: [],
                options: inOpts
            };
        },
        createElement: function (inJson, inCheckName, inParams, inPath, inType, inTypeId) {
            var type = inJson.type;
            var name = inJson.name;
            if (inCheckName && name) {
                inParams.namePathMap[name] = inPath;
            }
            if (type) {
                // the reference Component name
                return this.createComponent(type, inJson, inParams, inPath);
            } else {
                return  this.createElementByJson(inJson, inCheckName, inParams, inPath, inType, inTypeId);
            }
        },
        _parseComponentClass: function (inComponent,inBindings,inClassValue,inOptions) {
            var map = classValueToMap(inClassValue);
            var value,formatter,result,keys,length,name;
            var cls = inComponent["class"]();
            var element = inComponent.$();
            var def = {};

            for (name in map) {
                value = map[name];
                result = tryFormatter(value);
                formatter = result.formatter;
                if (formatter) {
                    keys = formatter.keys();
                    length = keys.length;
                    if (length > 1) {
                        throw "only support 1 key.";
                    } else {
                        value = formatter.formatObj(inOptions);
                        inBindings.push({
                            property: "class."+name,
                            formatter: formatter
                        });
                    }
                    def[name] = value;
                } else {
                    def[name] = value;
                }
                element.addClass(value);
            }
            cls.setMeta(def);

        },
        createComponent: function (inType, inJson, inParams, inPath) {
            var Component = util.isString(inType) ? getComponent(inType) : inType;
            var props = inJson.props;
            var name = null, result, formatter;
            var content = inJson.content;
            var childContent;
            var element;
            var i, length;
            var view;
            var metaData;
            var regionTarget;
            var regionName;
            var defaultRegion;
            var initValues = {};
            var bindings = [];
            var c = new Component;
            var def = {
                component: c,
                type: inType,
                path: inPath,
                bindings:bindings
            };

            if (props) {
                for (name in props) {
                    if (name === "class") {
                        this._parseComponentClass(c,bindings, props[name],inParams.options);
                        continue;
                    }
                    result = tryFormatter(props[name]);
                    formatter = result.formatter;
                    if (formatter) {
                        bindings.push({
                            property: name,
                            formatter: formatter
                        });
//                        def.bindings = bindings;
                    } else {
                        initValues[name] = result.value;
                        def.options = initValues;
                    }
                }
            }

            view = c.$view;
            metaData = view[VIEW_ENGINE_REMARK];
            defaultRegion = metaData.defaultRegion;
            if (content) {
                if (util.isString(content)) {
                    c.set("text", parseSimpleValue(content, inParams, inPath));
                } else if (util.isArray(content)) {
                    length = content.length;
                    for (i = 0; i < length; i++) {
                        childContent = content[i];
                        regionName = childContent.region;
                        element = this.createElement(childContent, true, inParams,
                            inPath + "." + i);
                        this.setRegion(c, regionName, element);
                    }
                } else {
                    regionName = content.region;
                    if (regionName) {
                        element = this.createElement(content, true, inParams, inPath
                            + ".0");
                        this.setRegion(c, regionName, element);
                    } else if (defaultRegion != undefined) {
                        element = this.createElement(content, true, inParams, inPath
                            + ".0");
                        regionTarget = byPath(c, defaultRegion);
                        element.appendTo(regionTarget);
                    } else {
                        throw "can not set content for " + inType;
                    }

                }

            }
            inParams.components.push(def);
            if (!inPath) {
                inParams.rootComponent = Component;
            }
            this.setEvents(inJson.events, inParams, inPath);
            return c;
        },
        setContent: function (inElement, inContent, inParams, inPath) {
            var i, length;
            var element;
            if (!inContent) {
                return;
            }
            if (util.isString(inContent)) {
                inElement.set("html", parseSimpleValue(inContent, inParams,
                    inPath));
            } else if (util.isArray(inContent)) {
                length = inContent.length;
                for (i = 0; i < length; i++) {
                    element = this.createElement(inContent[i], true,
                        inParams, inPath + "." + i);
                    element.appendTo(inElement);
                }
            } else {
                this.createElement(inContent, true, inParams,
                    inPath + ".0").appendTo(inElement);
            }
        },
        createElementByJson: function (inJson, inCheckName, inParams, inPath, inType, inTypeId) {
            var tag = inJson.tag || "div";
            var element, domElement;
            var template = inJson.template;
            var items = inJson.items;
            var jsProps = inJson.props;
            var array = [];
            var name = inJson.name;
            var region = inJson.region;
            var regions = inParams.regions;
            if (template) {
                if (!jsProps) {
                    jsProps = inJson.props = {};
                }
                jsProps.template = template;
                jsProps.items = items;
                delete inJson.template;
                delete inJson.items;
            }
            var props = this.getAttr(jsProps, inParams, inPath);

            if (region) {
                if (name) {
                    regions[name] = name;
                    inParams.hasRegion = true;
                } else {
                    // only one default region
                    inParams.defaultRegion = inPath;
                    inParams.hasRegion = true;
                }
                if (region === "default") {
                    inParams.defaultRegion = inPath;
                }
            }
            array.push("<", tag);
            if (inType) {
                array.push(' data-fruit-type="', inTypeId, fruit.DEBUG ? ':' + inType : '', '"');
            }
            array.push(props, "></", tag, ">");
            domElement = dom.createElement(array.join(""));
            element = new fruitElement(domElement);
            this.setContent(element, inJson.content, inParams, inPath);
            this.setEvents(inJson.events, inParams, inPath);
            return element;
        },
        setRegion: function (inComponent, inRegionName, inElement) {
            var container = inComponent._$[inRegionName];
            var regions, name = null;
            if (!container) {
                regions = inComponent.$view[VIEW_ENGINE_REMARK].regions;
                //get first element
                //TODO review this
                for (name in regions) {
                    break;
                }
                container = inComponent._$[name];
            }
            inElement.appendTo(container);
        },
        setEvents: function (inEvents, inParams, inPath) {
            var name, key;
            var result, formatter, keys, length;
            var events;
            var info;
            if (!inEvents) {
                return;
            }
            events = inParams.events;
            for (name in inEvents) {
                info = {
                    path: inPath,
                    eventType: name
                };
                key = inEvents[name];
                result = tryFormatter(key);
                var formatter = result.formatter;
                if (formatter) {
                    keys = formatter.keys();
                    length = keys.length;
                    if (length > 1) {
                        throw "only support 1 key.";
                    } else {
                        key = keys[0].path;
                    }
                }
                info.method = key;
                events.push(info);
            }
        },

        getAttr: function (inAttr, inParams, inPath) {
            if (!inAttr) {
                return "";
            }
            var name;
            var array = [];
            var value;
            for (name in inAttr) {
                value = inAttr[name];

                value = attrParser.parseAttr(value, inParams, inPath, name);
                if (value) {
                    array.push(" ", name, "=\"", value, "\"");
                }
            }
            return array.join("");
        }
    };

    fruit.define("fruit.ui.viewEngine", {
        singleton: true,
        methods: {
            create: function (inJson, inCache) {
                var component = fruit.ui.define({
                    view: inJson
                });
                return new component;


//                var type = inJson.type;
//                var Component;
//                if (type) {
//                    //create component with type;
//                    Component = getComponent(type);
//                    var params = parser.createInitParams();
//                    var component = new Component();
//                    parser.getAttr(inJson.props, params, "xx");
//                    parser.setEvents(inJson.events, params, "xx");
//                    this._setOptsBindings(component, params, component);
//                    this._setEvents(component, params);
//                    return component;
//                } else {
//                    Component = ui.define({view: inJson});
//                    if (inCache) {
//                        delete inJson[VIEW_ENGINE_REMARK];
//                    }
//                    return new Component();
//                }
            },
            dispose: function (inComponent) {
                var _$ = inComponent._$;
                var name;
                var element;
                var bindings = inComponent._bindings;
                var i, length = bindings ? bindings.length : 0;
                var root = inComponent.$();
                var parent;
                var result = null;
                for (name in _$) {
                    element = _$[name];
                    element.destroy();
                }
                inComponent._$ = null;
                for (i = 0; i < length; i++) {
                    bindings[i].destroy();
                }
                inComponent._bindings = null;
                if (root) {
                    parent = root.parent();
                    if (parent) {
                        result = {
                            parent: parent,
                            index: root.index()
                        };
                    }
                    root.destroy();
                    inComponent._element = null;
                }
                return result;
            },
            resolve: function (inComponent, inDomElement, inOwner) {
                var json = inComponent.$view;
                var clone = true;
                var view = null;
                var fruitType = null;
                if (dom.isElement(inDomElement)) {
                    view = new fruitElement(inDomElement);
                    view.owner(inOwner || inComponent);
                    clone = false;
                }
                var isFirst = false;
                var metaData;
                if (util.isFunction(json)) {
                    isFirst = true;
                    json = parser.fromFunction(inComponent.constructor, json,inComponent.constructor.$id);
                }
                else {
                    metaData = json[VIEW_ENGINE_REMARK];
                    if (!metaData || json.cache === false) {
                        isFirst = true;
                        parser.fromJSON(json, inComponent.$type, inComponent.constructor.$id);
                        metaData = json[VIEW_ENGINE_REMARK];
                    }
                }
                metaData = json[VIEW_ENGINE_REMARK];
                var element = metaData.element;
                if (clone) {
                    if (isFirst) {
                        metaData.element = new fruitElement(element._element.cloneNode(true));
                        element.owner(inOwner || inComponent);
                    } else {
                        var rootComponent = metaData.rootComponent;
                        if (rootComponent) {
                            element = rootComponent.create("_init", [element]);
                        } else {
                            element = new fruitElement(element._element.cloneNode(true));
                        }
                        element.owner(inOwner || inComponent);
                    }
                } else {
                    element = view;
                }

                inComponent._$ = {};
                inComponent._$["$root"] = element;

                this._resetComponents(inComponent, metaData);
                this._setCssClass(inComponent, metaData);
                this._setNamedComponent(inComponent, metaData);
                this._setOptsBindings(inComponent, metaData, null, inOwner);
                this._setEvents(inComponent, metaData);
                this._setRegion(inComponent, metaData);
                this._setElementOpts(inComponent, metaData);

                inComponent._pathElementMap = null;
                inComponent._element = element._element;
                if (fruit.DEBUG) {
                    element.setAttribute("data-fruit-id", element.$id);
                }
            },
            _setRegion: function (inComponent, inMetaData) {
                if (inMetaData.hasRegion && !inComponent.setRegions) {
                    inComponent.setRegion = dynamicMethods.setRegion;
                }
            },
            _setEvents: function (inComponent, inMetaData) {
                var events = inMetaData.events;
                var info, i, length, element;
                var method, path;
                if (events) {
                    length = events.length;
                    for (i = 0; i < length; i++) {
                        info = events[i];
                        method = info.method;
                        path = info.path;
                        element = byPath(inComponent, path);
                        this._setBinding(element, info.eventType, method, null);
                    }
                }
            },
            _setCssClass: function (inComponent, inMetaData) {
                var cssClassMap = inMetaData.cssClassMap;
                var path, element, cssClass;
                for (path in cssClassMap) {
                    element = byPath(inComponent, path);
                    cssClass = element['class']();
                    cssClass.setMeta(cssClassMap[path]);
                }
            },
            _setOptsBindings: function (inComponent, inMetaData, inSource, inOwner) {
                var formatterList = inMetaData.formatterList;
                var formatter, target, targetPath, element;
                var info, sourcePath;
                var propertyName;
                var subPropertyName;
                var i, length = formatterList.length;
                for (i = 0; i < length; i++) {
                    info = formatterList[i];
                    propertyName = info.targetProperty;
                    sourcePath = info.sourcePath;
                    formatter = info.formatter;
                    element = byPath(inComponent, info.targetElementPath);
                    element.owner(inOwner || inComponent);
                    if (info.subProperty) {
                        target = element.get(propertyName);
                        subPropertyName = info.subProperty;
                        this._setBinding(element, propertyName + "." + subPropertyName, sourcePath, formatter);
                    } else {
                        //simple property
                        targetPath = propertyName || "content";
                        target = inComponent;
                        this._setBinding((inSource ? inSource : element), targetPath, sourcePath, formatter);
                    }
                }
            },
            _setElementOpts: function (inComponent, inMetaData) {
                var elementPropertyMap = inMetaData.elementPropertyMap;
                var path, opts, element;
                for (path in elementPropertyMap) {
                    opts = elementPropertyMap[path];
                    element = byPath(inComponent, path);
                    element.setAll(opts);
                }
            },
            _setNamedComponent: function (inComponent, metaData) {
                var elements = inComponent._$;
                var map = metaData.namePathMap;
                var regions = metaData.regions;
                var name, component;
                for (name in map) {
                    elements[name] = component = byPath(inComponent, map[name]);
                    if (fruit.DEBUG) {
                        component.setAttribute("data-fruit-name", name);
                    }
                    if (regions && regions[name]) {
                        component.owner(null);
                    } else {
                        component.owner(inComponent);
                    }
                }
                inComponent.$ = dynamicMethods.find;
            },
            _resetComponents: function (inComponent, inMetaData) {
                var i, length, element;
                var components = inMetaData.components;
                var def, path, type, Type, component;
                length = components.length;
                for (i = 0; i < length; i++) {
                    def = components[i];
                    path = def.path;
                    element = byPath(inComponent, path);
                    element.setAll(def.options);
                    this._setComponentBindings(element, inComponent, def.bindings);
                    var map = inComponent._pathElementMap = inComponent._pathElementMap || {};
                    map[path] = element;
                }
            },
            _setComponentBindings: function (inTarget, inComponent, inBindings) {
                var i, length, binding, formatter, key;
                if (!inBindings) {
                    return;
                }

                length = inBindings.length;
                for (i = 0; i < length; i++) {
                    binding = inBindings[i];
                    formatter = binding.formatter;
                    key = binding.property;
                    this._setBinding(inTarget, key, formatter.keys()[0].path, formatter);
                }
            },
            _setBinding: function (inTarget, inTargetPath, inSourcePath, inFormatter) {
                var index = inTargetPath.lastIndexOf(".");
                var target = inTarget;
                var targetPath = inTargetPath;
                if (!target || !targetPath) {
                    return;
                }
                if (index > 0) {
                    target = inTarget.getByPath(inTargetPath.substr(0, index));
                    targetPath = inTargetPath.substr(index + 1);
                }
                target.setBinding(targetPath, inSourcePath, {
                    source: inTarget,
                    formatter: inFormatter ? (inFormatter.onlyKey() ? null : inFormatter) : null
                });
            }
        }
    });


    var getComponent = function (inName) {
        var Component = util.getByPath(inName);
        if (!Component) {
            throw "can not find Component:" + inName;
        }
        return Component;
    };

    var byPath = function (inComponent, inPath) {
        var map = inComponent._pathElementMap = inComponent._pathElementMap
            || {};
        var element = map[inPath];
        if (!element) {
            element = getElementByPath(inComponent._$["$root"], inPath);
            map[inPath] = element;
        }
        return element;
    };
    var getElementByPath = function (inElement, inPath) {
        var array = inPath.split(".");
        var i = 1;
        var length = array.length, index;
        var element = inElement;
        for (; i < length; i++) {
            index = array[i];
            element = element.childAt(index);
        }

        return element;
    };

    var ViewExtendContext = fruit.define({
        methods: {
            init: function (inJson) {
                this._json = util.clone(inJson);
            },
            getJson: function () {
                return this._json;
            },
            $: function (inName) {
                var json = this._json;
                if (!inName) {
                    return json;
                }
                return this._name(json, inName);
            },
            _name: function (inContent, inName) {
                var child, content, i, length, result;
                if (!inContent) {
                    return;
                }
                content = inContent.content;
                if (!content) {
                    return;
                }
                if (util.isArray(content)) {
                    length = content.length;
                    for (i = 0; i < length; i++) {
                        child = content[i];
                        if (child.name === inName) {
                            return child;
                        } else {
                            result = this._name(child, inName);
                            if (result) {
                                return result;
                            }
                        }
                    }
                } else {
                    if (content.name === inName) {
                        return content;
                    } else {
                        return this._name(content, inName);
                    }
                }
            },
            append: function (inChild, inName) {
                var parent = this.$(inName);
                var child = inChild || {};
                if (!parent) {
                    throw "can not find element definition[name=" + inName
                        + "]";
                }

                var content = parent.content;
                if (!content) {
                    content = child;
                } else if (util.isArray(content)) {
                    content.push(child);
                } else if (util.isString(content)) {
                    throw "Can not append";
                } else {
                    content = [ content, child ];
                }
                parent.content = content;
                return child;
            },
            regions: function (inCfg) {
                var region, content, container;
                if (inCfg) {
                    for (region in inCfg) {
                        container = this.$(region);
                        if (!container) {
                            throw "region[" + region + "] not found";
                        }
                        content = inCfg[region];
                        container.content = content;
                    }
                }
            },
            insert: function (inIndex, inChild, inName) {
                var parent = this.$(inName);
                if (!parent) {
                    throw "can not find element definition[name=" + inName
                        + "]";
                }
                var child = inChild || {};
                var content = parent.content;
                if (!content) {
                    content = child;
                } else if (util.isArray(content)) {
                    if (inIndex < content.length) {
                        content.splice(inIndex, 0, child);
                    } else {
                        content.push(child);
                    }
                } else if (util.isString(content)) {
                    throw "Can not insert";
                } else {
                    if (inIndex) {
                        content = [ content, child ];
                    } else {
                        content = [ child, content ];
                    }
                }
                parent.content = content;
                return child;
            }
        }
    });
})
    (window, undefined, fruit, fruit.Binding, fruit.util, fruit.Formatter,
        fruit.ui.Element, fruit.dom, fruit.ui);
